package calpi.database;

import java.io.IOException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.LinkedList;
import calpi.Curso;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.util.ArrayList;

/**
 * Classe responsável pela leitura e gravação dos registros dos cursos
 * @author Sem iNick
 */
public class CursosConnector extends DataBaseConnector<Curso> {

    //Arquivo padrão de gravação e leitura
    public static final File DEFAULT_FILE = new File("cursos.rdb");

    //Streams que serão usados
    private DataInputStream inStream;
    
    /**
     * Cria uma conexão com os dados dos cursos usandos um arquivo de entrada e
     * um de saída
     * @param fileIn Objeto relacionado ao arquivo que será usado como
     * entrada de dados
     * @param fileOut Objeto relacionada ao arquivo que será usada como saída
     * de dados
     * @throws FileNotFoundException Se algum arquivo não existir e não puder
     * ser criado
     * @throws IOException Se ocorrer algum error E/S
     */
    public CursosConnector(File fileIn, File fileOut)
            throws FileNotFoundException, IOException {

        super(fileIn, fileOut);
        inStream = new DataInputStream(new FileInputStream(fileIn));
    }

    /**
     * Cria uma conexão com os dados do curso usando o caminho para um arquivo
     * de entrada e outro de saída
     * @param fileIn Caminho para o arquivo de entrada
     * @param fileOut Caminho para o arquivo de saída
     * @throws FileNotFoundException Se algum arquivo não existir e não puder
     * ser criado
     * @throws IOException Se ocorrer algum erro E/S
     */
    public CursosConnector(String fileIn, String fileOut)
            throws FileNotFoundException, IOException {

        this(new File(fileIn), new File(fileOut));
    }

    /**
     * Cria uma conexão com os dados dos cursos usando um arquivo com entrada e
     * saída de dados
     * @param file Objeto relacionado com o arquivo que será usado como entrada
     * e saída
     * @throws FileNotFoundException Se o arquivo não existir e não puder ser
     * criado
     * @throws IOException Se ocorrer algum erro E/S
     */
    public CursosConnector(File file) throws FileNotFoundException, IOException {

        this(file, file);
    }

    /**
     * Cria uma conexão com os dados dos cursos usando um caminho para um arquivo
     * que será usado com entrad e saída de dados
     * @param file Caminho onde se encontra o arquivo que será usado
     * @throws FileNotFoundException Se o arquivo não existir e não puder ser
     * criado
     * @throws IOException Se ocorrer algum erro E/S
     */
    public CursosConnector(String file) throws FileNotFoundException, IOException {

        this(new File(file));
    }

    /**
     * Cria uma conexão com os dados dos cursos usando o arquivo padrão como
     * entrada e saída
     * @throws FileNotFoundException Se o arquivo não existir e não puder ser
     * criado
     * @throws IOException Se ocorrer algum error E/S
     */
    public CursosConnector() throws FileNotFoundException, IOException {

        this(DEFAULT_FILE);
    }

    /**
     * Fecha a conexão com a base de dados
     * @throws IOException Se ocorrer algum erro E/S no fechamento do streams
     */
    @Override
    public void closeConnection() throws IOException {

        if (inStream != null) {

            inStream.close();
        }
    }

    /**
     * Garante que os streams serão fechados quando não houver mais referência
     * ao objeto
     * @throws IOException Se ocorrer algum erro na hora do fechamento
     */
    @Override
    protected void finalize() throws IOException {

        closeConnection();
    }

    /**
     * Lê as informações do arquivo de entrada e as colaca no buffer de entrada
     * para consuta e no buffer de saída para modificação e futuras escritas
     * @throws IOException Se ocorrer algum erro de E/S
     */
    @Override
    public synchronized void readFully() throws IOException {

        LinkedList<Curso> buffer = new LinkedList<Curso>();
        
        try {

            while (true) {

                buffer.add(new Curso(inStream.readInt(), inStream.readUTF()));
            }
        } catch (EOFException eofe) {

            //End of file nothing to do ^^
        }

        bufferIn = new Curso[buffer.size()];
        buffer.toArray(bufferIn);
        bufferOut = new ArrayList<Curso>(buffer);
    }

    /**
     * Escreve os dados que estão no buffer de saída para o arquivo de saída.
     * Os dados serão escritos na ordem em que se encontram no buffer de saída.
     * Caso queira organizá-los, use o método de organização do buffer de saída
     * @throws IOException Se ocorrer algum error na hora da escrita
     * @see DataBaseConnector#sortBufferOut()
     */
    @Override
    public synchronized void writeBuffer() throws IOException {

        DataOutputStream outStream = new DataOutputStream(new FileOutputStream(getFileOut()));
        for(Curso curso : bufferOut) {

            outStream.writeInt(curso.getId());
            outStream.writeUTF(curso.getNome());
        }

        //Atualiza o buffer de entrada
        bufferIn = new Curso[bufferOut.size()];
        bufferOut.toArray(bufferIn);

        outStream.flush();
        outStream.close();
    }

    /**
     * Procura algum curso no buffer de saída que tenho o ID igual ao passado
     * @param id ID que será procurado
     * @return O curso com o ID igual ao passado ou <code>null</code> se não exitir
     * tal curso
     */
    public Curso getCursoById(int id) {

        for (Curso c : bufferOut) {

            if (c.getId() == id) {

                return c;
            }
        }

        return null;
    }

    /**
     * Procura algum curso no buffer de saída com base no nome do curso
     * @param id Nome do curso que será procurado
     * @return O curso com o nome igual ao passado ou <code>null</code> se não exitir
     * tal curso
     */
    public Curso getCursoByNome(String nome) {

        for (Curso c : bufferOut) {

            if (c.getNome().equals(nome)) {

                return c;
            }
        }

        return null;
    }
}
